<?php


namespace EchoPHP\Database\TuxedoDriver;


use EchoPHP\Support\Str;

class Handler {

    /**
     * tuxedo客户端日志， 不定义默认值 /www/logs/tuxedo/TUXEDO_ULOG
     * @var
     */
    protected $logPath;

    /**
     * tuxedo服务端配置文件地址  不定义默认值 /www/tux.env
     * @var
     */
    protected $hostConfig;

    /**
     * tuxedo服务名
     * @var
     */
    private $serverName;
    /**
     * 字符串分隔符
     * @var string
     */
    private $sign;
    /**
     * 发送给tuxedo服务端的字符串
     * @var string
     */
    private $sendBuffer = '';
    /**
     * tuxedo服务端返回的字符串
     * @var string
     */
    private $receiveBuffer = '';
    /**
     * tuxedo服务端返回串解析游标位置
     * @var int
     */
    protected $cursor = 0;
    /**
     * tuxedo服务实际执行时间（从客户端发起到tuxedo返回结果时间）
     * @var int
     */
    protected $realTime = 0;

    /**
     * 查询语句返回记录列数，即每条记录字段数目
     * @var int
     */
    protected $colCount = 0;
    /**
     * 查询语句返回记录行数，即总记录数
     * @var int
     */
    protected $rowCount = 0;
    /**
     * 记录字段名
     * @var array
     */
    protected $fieldNames = [];
    /**
     * 字段名大小写强制
     */
    //todo - 指定字段名大小写为默认小写，通过配置项修改逻辑暂不提供
    protected $fieldCase = 2;
    /**
     * 强制列名为指定的大小写
     */
    const ATTR_CASE = 8;
    /**
     * 强制列名小写
     */
    const CASE_LOWER = 2;
    /**
     * 强制列名大写
     */
    const CASE_UPPER = 1;
    /**
     * 保留tuxedo服务返回的列名。
     */
    const CASE_NATURAL = 0;
    /**
     * 数据提取模式
     */
    protected $fetchMode = 2;
    /**
     * 设置默认的提取模式
     */
    const ATTR_DEFAULT_FETCH_MODE = 19;
    /**
     * 将对应结果集中的每一行作为一个由列名索引的数组返回
     */
    const FETCH_ASSOC = 2;
    /**
     * 将对应结果集中的每一行作为一个由列号索引的数组返回
     */
    const FETCH_NUM = 3;
    /**
     * 将结果集中的每一行作为一个属性名对应列名的对象返回
     */
    const FETCH_OBJ = 5;

    /**
     * 最后插入id
     * @var string
     */
    protected $lastInsertId = '';
    /**
     * 影响记录行数
     * @var int
     */
    protected $affectedRows = 0;

    /**
     * 执行错误信息
     * @var int
     */
    protected $errorCode = 0;
    protected $errorInfo = '';
    protected $clientErrors = [
        0 => '调用成功',
        -1 => '调用tux_call的参数个数不正确',
        -2 => '未定义服务名或者sendBuffer为空',
        -3 => '分配TPINIT的缓冲区失败，详细请参看tuxedo客户端日志',
        -4 => 'tpinit失败，详细请参看tuxedo客户端日志',
        -5 => '分配tpRecvBuf的缓冲区失败，详细请参看tuxedo客户端日志',
        -6 => '分配tpSendBuf的缓冲区失败，详细请参看tuxedo客户端日志',
        -7 => '调用tuxedo服务端出现错误，详细请参看tuxedo客户端日志',
        -8 => '拷贝tpRecvBuf的内容到zRetBuf出现错误，详细请参看tuxedo客户端日志',
    ];

    public function __construct() {
        $this->sign = config('database.tuxedo_sign', chr(26));
    }

    /**
     * 获取错误码
     * @return int
     */
    public function getErrorCode() {
        return $this->errorCode;
    }

    /**
     * 获取错误信息
     * @return string
     */
    public function getErrorInfo() {
        return $this->errorInfo;
    }

    /**
     * 设置客户端日志文件路径
     * @param $logPath
     */
    public function setLogPath($logPath) {
        $this->logPath = $logPath;
    }

    /**
     * 偶去客户端日志文件路径
     * @return string
     */
    public function getLogPath() {
        return $this->logPath ?: '/www/logs/tuxedo/TUXEDO_ULOG';
    }

    /**
     * 设置服务端配置文件
     * @param $hostConfig
     */
    public function setHostConfig($hostConfig) {
        $this->hostConfig = $hostConfig;
    }

    /**
     * 获取服务端配置文件
     * @return string
     */
    public function getHostConfig() {
        return $this->hostConfig ?: '/www/tux.env';
    }

    /**
     * 调用tuxedo服务端
     * @return mixed
     */
    protected function callTuxedo() {
        $timeStart = microtime(1);
        $originSendBuf = ' ' . $this->sendBuffer;
        $executeResult = tux_call($this->serverName, $this->sendBuffer, $this->receiveBuffer, 1048576, $this->logPath, $this->hostConfig);

        $this->realTime = round((microtime(1) - $timeStart) * 1000, '2');

        //解析tuxedo客户端执行结果错误信息
        if ($executeResult != 0) {
            $this->errorCode = $executeResult;
            $this->errorInfo = isset($this->clientErrors[$executeResult]) ? $this->clientErrors[$executeResult] : '未知错误';
            throw new TuxedoException($this->errorInfo, $this->errorCode, $this->serverName, $originSendBuf, $this->receiveBuffer);
        }
        //解析tuxedo服务端返回结果
        if (substr($this->receiveBuffer, -1) != $this->sign) {
            //所有返回结果以chr(26)结束,不使用trim函数进行处理，防止多个个分隔符结尾时被过滤
            $this->receiveBuffer .= $this->sign;
        }
        //解析sql执行结果，如果失败，解析失败原因和错误码
        $this->parseCallResult();

        $serverInfo = explode('_', $this->serverName);
        if ($serverInfo[1] == 'SEL') {
            $this->parseSelRowAndColCount();
            $this->parseSelFieldName();
        } elseif ($serverInfo[1] == 'UPD') {
            $this->parseUpdAffectRows();
        } else {
            $this->parseBatInfo();
        }

        return true;
    }

    /**
     * 设置tuxedo服务名
     * @param $serverName
     */
    public function setServerName($serverName) {
        $this->serverName = $serverName;
    }

    /**
     * 获取tuxedo服务名
     * @return mixed
     */
    public function getServerName() {
        return $this->serverName;
    }

    /**
     * 获取字符串分隔符
     * @return \EchoPHP\Foundation\Container|mixed|object|string
     */
    public function getSign() {
        return $this->sign;
    }

    /**
     * 设置发送串
     * @param string $sendBuffer
     */
    public function setSendBuffer(string $sendBuffer) {
        $this->sendBuffer = $sendBuffer;
    }

    /**
     * 获取发送给tuxedo服务端的串
     * @return string
     */
    public function getSendBuffer() {
        return $this->sendBuffer;
    }

    /**
     * 设置数据集遍历模式
     * @param int $mode
     */
    public function setFetchMode(int $mode) {
        if (in_array($mode, [self::FETCH_ASSOC, self::FETCH_NUM, self::FETCH_OBJ], true)) {
            $this->fetchMode = $mode;
        }
    }

    /**
     * 设置字段名强制大小写规则
     * @param $case
     */
    public function setFieldCase($case) {
        if (in_array($case, [self::CASE_LOWER, self::CASE_UPPER, self::CASE_NATURAL], true)) {
            $this->fieldCase = $case;
        }
    }

    /**
     * 向发送串结尾添加新的绑定内容
     * @param $value
     */
    public function pushSendBuffer($value) {
        if (!is_array($value)) {
            $value = str_replace($this->sign, ' ', $value);
            if (!empty($this->sendBuffer) && substr($this->sendBuffer, -1, 1) != $this->sign) {
                //防止不是拼接符结尾的情况
                $this->sendBuffer .= $this->sign;
            }
            $this->sendBuffer .= Str::convertCharset($value, 'GBK') . $this->sign;
        } else {
            foreach ($value as $item) {
                $this->pushSendBuffer($item);
            }
        }
    }

    /**
     * tuxedo返回串解析,一次解析一条
     * @return bool|string
     */
    protected function parseReceiveBuffer() {
        if (false === ($position = strpos($this->receiveBuffer, $this->sign, $this->cursor))) {
            return false;
        }
        $content = substr($this->receiveBuffer, $this->cursor, $position - $this->cursor);
        $this->cursor = $position + 1;

        return Str::convertCharset($content, 'UTF-8', 'GBK');
    }

    /**
     * 获取tuxedo返回串
     * @return string
     */
    public function getReceiveBuffer() {
        return $this->receiveBuffer;
    }

    /**
     * 获取tuxedo实际执行耗时
     * @return int
     */
    public function getRealTime() {
        return $this->realTime;
    }

    /**
     * 解析tuxedo服务端返回的调用结果
     */
    protected function parseCallResult() {
        if (false === ($content = $this->parseReceiveBuffer())) {
            $this->errorCode = -99;
            $this->errorInfo = 'ReceiveBuffer为空,未解析到执行结果';
            throw new TuxedoException($this->errorInfo, $this->errorCode, $this->serverName, $this->sendBuffer, $this->receiveBuffer);
        }
        if ($content == 0) {
            //错误信息处理逻辑，返回串的错误信息 格式  0 -> 错误码 -> 错误信息
            if (false === ($content = $this->parseReceiveBuffer())) {
                $this->errorCode = -99;
                $this->errorInfo = 'ReceiveBuffer未返回错误码';
                throw new TuxedoException($this->errorInfo, $this->errorCode, $this->serverName, $this->sendBuffer, $this->receiveBuffer);
            }
            $this->errorCode = $content;
            if (false === ($content = $this->parseReceiveBuffer())) {
                $this->errorInfo = 'ReceiveBuffer未返回错误信息';
                throw new TuxedoException($this->errorInfo, $this->errorCode, $this->serverName, $this->sendBuffer, $this->receiveBuffer);
            }
            $this->errorInfo = $content;
            throw new TuxedoException($this->errorInfo, $this->errorCode, $this->serverName, $this->sendBuffer, $this->receiveBuffer);
        }
    }

    /**
     * 解析查询结果集中的行和列数目
     * @return bool
     */
    protected function parseSelRowAndColCount() {
        if (false === ($content = $this->parseReceiveBuffer())) {
            $this->errorCode = -99;
            $this->errorInfo = '未解析到rowcount';
            throw new TuxedoException($this->errorInfo, $this->errorCode, $this->serverName, $this->sendBuffer, $this->receiveBuffer);
        }
        $this->rowCount = intval($content);
        if (false === ($content = $this->parseReceiveBuffer())) {
            $this->errorCode = -99;
            $this->errorInfo = '未解析到colcount';
            throw new TuxedoException($this->errorInfo, $this->errorCode, $this->serverName, $this->sendBuffer, $this->receiveBuffer);
        }
        $this->colCount = intval($content);

        return true;
    }

    /**
     * 解析结果集字段名
     * @return bool
     */
    protected function parseSelFieldName() {
        for ($i = 1; $i <= $this->colCount; $i++) {
            if (false === ($content = $this->parseReceiveBuffer())) {
                $this->errorCode = -99;
                $this->errorInfo = '解析第' . $i . '个字段名出错';
                throw new TuxedoException($this->errorInfo, $this->errorCode, $this->serverName, $this->sendBuffer, $this->receiveBuffer);
            }
            if ($this->fieldCase === self::CASE_UPPER) {
                $content = strtoupper($content);
            } elseif ($this->fieldCase === self::CASE_LOWER) {
                $content = strtolower($content);
            }
            array_push($this->fieldNames, $content);
        }
        return true;
    }

    /**
     * 解析更新类操作受影响函数，并尝试解析是否能获取到最后新增id
     * @return bool
     */
    protected function parseUpdAffectRows() {
        if (false === ($content = $this->parseReceiveBuffer())) {
            $this->errorCode = -99;
            $this->errorInfo = '未解析到affectrows';
            throw new TuxedoException($this->errorInfo, $this->errorCode, $this->serverName, $this->sendBuffer, $this->receiveBuffer);
        }
        $this->affectedRows = intval($content);
        if (false !== ($content = $this->parseReceiveBuffer())) {
            $this->lastInsertId = $content;
        } else {
            $this->lastInsertId = '';
        }
        return true;
    }

    /**
     * 解析事务操作执行信息
     * @return bool
     */
    protected function parseBatInfo() {
        if (false === ($content = $this->parseReceiveBuffer())) {
            $this->errorCode = -99;
            $this->errorInfo = '未解析到事务执行结果';
            throw new TuxedoException($this->errorInfo, $this->errorCode, $this->serverName, $this->sendBuffer, $this->receiveBuffer);
        }
        $this->errorInfo = $content;
        return true;
    }


}